package se581.banker.listener;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;

import se581.banker.service.RegistrationService;
import se581.banker.util.ServerSocketInfo;

/**
 * This class tells the connect information for use of the Update and
 * Inquiry Trackers.  This is provided in the form of host:port.  E.g. localhost:6789
 * 
 * The user is also registered here for a callback if/when a failover occurs.
 * 
 * @author mmichalak
 *
 */
public class RegistrationListener implements Runnable {
	static Logger logger = Logger.getLogger(RegistrationListener.class.getName());
	
	RegistrationService regSvc = null;
	
	/**
	 * Contains Tracker system users to notify upon failover
	 * Key = host:port, value=timestamp
	 */
	Map<String, String> registeredUsers = new HashMap<String, String>();
	
	int socketPort = -1;
	
	//TODO TEMP - change to be primary and back
	String updateSvrInfo = null;
	String inquirySvrInfo = null;
		
	
	@Override
	public void run() {
		logger.debug("Starting RegistrationListener thread on port " + socketPort); 

		try {
			String regRequest;
			  
			  ServerSocket welcomeSocket = new ServerSocket(socketPort);
			  welcomeSocket.setReuseAddress(true);

			  while(true) 
			  {
			     Socket connectionSocket = welcomeSocket.accept();

			     BufferedReader inFromClient =
			        new BufferedReader(
			        new InputStreamReader(
			        connectionSocket.getInputStream()));

			     DataOutputStream outToClient =
			        new DataOutputStream(
			        connectionSocket.getOutputStream());

			    try {
			    	regRequest = inFromClient.readLine();
				} catch (SocketException e) {
					// Client reset socket
					logger.warn("SocketException caught.  Client reset socket.");
				    continue;
				}
				 logger.debug("rec'd:  " + regRequest);

				 
			     String regReturnInfo = null;
			     if (regRequest.contains("SERVER"))
			    	 regReturnInfo = registerServer(regRequest);
			     else 
			    	 regReturnInfo = registerUser(regRequest);
			     
				 outToClient.writeBytes(regReturnInfo + '\n');
			     
			  }
		} catch (IOException e) {
			logger.error("Error in UpdateTracker thread");
			e.printStackTrace();
		}

	}
	
	/**
	 * Returns the next available server for the client using round-robin
	 * The following is currently not being used:
	 * Registers to the user's host and port to be used for a failover callback
	 * 
	 * @param requestedSvc - UPDATE|localhost:9999   E.g. the client's info
	 * @return E.g. localhost:6789
	 */
	String registerUser(String requestedSvc) {
		logger.debug("Beginning registerUser...");
		String svc = null;
		
		String[] tokens = requestedSvc.split(",");
		if (tokens[0] != null) {
				if ("UPDATE".equalsIgnoreCase(tokens[0]))
					svc = regSvc.returnNextUpdateTracker().toSerializeString();
				else if ("INQUIRY".equalsIgnoreCase(tokens[0]))
					svc = regSvc.returnNextInquiryTracker().toSerializeString();
		}
		
		if (svc == null) {
			logger.error("Requested service not found - " + requestedSvc);
			return null;
		}
		
		
		// Register the user for notification in case a server fails.  They will be
		// given a new server to use
		registeredUsers.put( tokens[1], new Date().toString() );
		logger.debug("++++++++++++++++++++++++++++++ next server " + svc + "for request " + requestedSvc);		
		return svc;
	}
	
	/**
	 * 
	 * @param newServer - Input:  SERVER,ip:Update_Port:Query_Port
	 * @return true if new server was successully registered
	 */
	String registerServer(String newServer) {
		logger.debug("Beginning registerServer...");
		String svc = null;
		
		String[] tokens = newServer.split(",");
		if (tokens[0] == null || !"SERVER".equalsIgnoreCase(tokens[0])) {
			logger.error("SERVER registration request was malformed - " + newServer);
			return null;
		}
				
		String[] tokens2 = tokens[1].split(":");
		ServerSocketInfo updateServerInfo = new ServerSocketInfo();
		updateServerInfo.setHost(tokens2[0]); 
		updateServerInfo.setPort(tokens2[1]); // Update port
		
		ServerSocketInfo queryServerInfo = new ServerSocketInfo();
		queryServerInfo.setHost(tokens2[0]);
		queryServerInfo.setPort(tokens2[2]); // Query port
			
		regSvc.registerServer(updateServerInfo, queryServerInfo);
		
		String ret = "SUCCESS " + newServer + "\n";
		logger.debug("=========================  " + ret);		
		return ret;
	}
	

	public RegistrationService getRegSvc() {
		return regSvc;
	}

	public void setRegSvc(RegistrationService regSvc) {
		this.regSvc = regSvc;
	}

	public Map<String, String> getRegisteredUsers() {
		return registeredUsers;
	}

	public void setRegisteredUsers(Map<String, String> registeredUsers) {
		this.registeredUsers = registeredUsers;
	}

	public int getSocketPort() {
		return socketPort;
	}

	public void setSocketPort(int socketPort) {
		this.socketPort = socketPort;
	}

	public String getUpdateSvrInfo() {
		return updateSvrInfo;
	}

	public void setUpdateSvrInfo(String updateSvrInfo) {
		this.updateSvrInfo = updateSvrInfo;
	}

	public String getInquirySvrInfo() {
		return inquirySvrInfo;
	}

	public void setInquirySvrInfo(String inquirySvrInfo) {
		this.inquirySvrInfo = inquirySvrInfo;
	}

}
